#include "redis_handle.h"
#include <iostream>

RedisORM* RedisORM::instance = nullptr;

RedisORM::RedisORM(const std::string& host, int port) {
    context = redisConnect(host.c_str(), port);
    if (context == nullptr || context->err) {
        if (context) {
            std::cerr << "Error: " << context->errstr << std::endl;
            redisFree(context);
        } else {
            std::cerr << "Can't allocate redis context" << std::endl;
        }
    }
}

RedisORM::~RedisORM() {
    if (context != nullptr) {
        redisFree(context);
    }
}

RedisORM* RedisORM::getInstance(const std::string& host, int port) {
    if (instance == nullptr) {
        instance = new RedisORM(host, port);
    }
    return instance;
}

bool RedisORM::insert(const std::string& key, const std::string& value) {
    if (context == nullptr) {
        return false;
    }
    redisReply* reply = static_cast<redisReply*>(redisCommand(context, "SET %s %s", key.c_str(), value.c_str()));
    if (reply == nullptr) {
        return false;
    }
    bool result = (reply->type == REDIS_REPLY_STATUS && std::string(reply->str) == "OK");
    freeReplyObject(reply);
    return result;
}

bool RedisORM::remove(const std::string& key) {
    if (context == nullptr) {
        return false;
    }
    redisReply* reply = static_cast<redisReply*>(redisCommand(context, "DEL %s", key.c_str()));
    if (reply == nullptr) {
        return false;
    }
    bool result = (reply->type == REDIS_REPLY_INTEGER && reply->integer > 0);
    freeReplyObject(reply);
    return result;
}

std::string RedisORM::select(const std::string& key) {
    if (context == nullptr) {
        return "";
    }
    redisReply* reply = static_cast<redisReply*>(redisCommand(context, "GET %s", key.c_str()));
    if (reply == nullptr || reply->type != REDIS_REPLY_STRING) {
        if (reply) {
            freeReplyObject(reply);
        }
        return "";
    }
    std::string result = reply->str;
    freeReplyObject(reply);
    return result;
}

bool RedisORM::update(const std::string& key, const std::string& new_value) {
    return insert(key, new_value);
}

bool RedisORM::insertHashField(const std::string& hashKey, const std::string& field, const std::string& value) {
    if (context == nullptr) {
        return false;
    }
    redisReply* reply = static_cast<redisReply*>(redisCommand(context, "HSET %s %s %s", hashKey.c_str(), field.c_str(), value.c_str()));
    if (reply == nullptr) {
        return false;
    }
    bool result = (reply->type == REDIS_REPLY_INTEGER && reply->integer > 0);
    freeReplyObject(reply);
    return result;
}

std::string RedisORM::selectHashField(const std::string& hashKey, const std::string& field) {
    if (context == nullptr) {
        return "";
    }
    redisReply* reply = static_cast<redisReply*>(redisCommand(context, "HGET %s %s", hashKey.c_str(), field.c_str()));
    if (reply == nullptr || reply->type != REDIS_REPLY_STRING) {
        if (reply) {
            freeReplyObject(reply);
        }
        return "";
    }
    std::string result = reply->str;
    freeReplyObject(reply);
    return result;
}

bool RedisORM::updateHashField(const std::string& hashKey, const std::string& field, const std::string& new_value) {
    return insertHashField(hashKey, field, new_value);
}

bool RedisORM::removeHashField(const std::string& hashKey, const std::string& field) {
    if (context == nullptr) {
        return false;
    }
    redisReply* reply = static_cast<redisReply*>(redisCommand(context, "HDEL %s %s", hashKey.c_str(), field.c_str()));
    if (reply == nullptr) {
        return false;
    }
    bool result = (reply->type == REDIS_REPLY_INTEGER && reply->integer > 0);
    freeReplyObject(reply);
    return result;
}